#pragma once

#include <iostream>
#include <string>
#include <sstream>
#include <unordered_map>
#include <algorithm>
#include <vector>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include "Util.hpp"
#include "Log.hpp"

#define SEP ": "
#define WEB_ROOT "wwwroot"
#define HOME_PAGE "index.html"
#define HTTP_VERSION "HTTP/1.0"
#define LINE_END "\r\n"

#define OK 200
#define NOT_FOUND 404
// #define DEBUG
using std::cout;
using std::endl;
using std::string;
using std::unordered_map;
using std::vector;

static string Code2Desc(int code)
{
    string desc;
    switch (code)
    {
    case 200:
        desc = "OK";
        break;
    case 404:
        desc = "Not Found";
        break;
    default:
        break;
    }
    return desc;
}

class HttpRequest
{
public:
    string request_line;
    vector<string> request_header;
    string blank;
    string reques_body;

    // 解析完毕后的结果
    string method;
    string uri; // path args
    string version;

    unordered_map<string, string> header_kv;
    int content_length;

    string path;
    string query_string;

    bool cgi;

public:
    HttpRequest() : content_length(0), cgi(false) {}
    ~HttpRequest() {}
};

class HttpResponse
{
public:
    string status_line;
    vector<string> response_header;
    string blank;
    string response_body;

    int status_code;
    int fd;
    int size;

public:
    HttpResponse() : status_code(OK), blank(LINE_END), fd(-1) {}
    ~HttpResponse() {}
};

// 读取请求 分析请求 构建响应 发送响应
class EndPoint
{
private:
    int sock;
    HttpRequest http_request;
    HttpResponse http_response;

private:
    void RecvHttpRequestLine()
    {
        auto &line = http_request.request_line;
        Util::ReadLine(sock, line);
        line.resize(line.size() - 1);
        LOG(INFO, line);
    }

    void RecvHttpRequestHeader()
    {
        string line;
        while (1)
        {
            line.clear();
            Util::ReadLine(sock, line);
            if (line == "\n")
            {
                http_request.blank = line;
                break;
            }
            line.resize(line.size() - 1);
            http_request.request_header.push_back(line);
            LOG(INFO, line);
        }
    }

    void ParseHttpRequestLine()
    {
        auto &line = http_request.request_line;
        std::stringstream ans(line);
        ans >> http_request.method >> http_request.uri >> http_request.version;

        auto &method = http_request.method;
        std::transform(method.begin(), method.end(), method.begin(), ::toupper);
        // LOG(INFO, http_request.method);
        // LOG(INFO, http_request.uri);
        // LOG(INFO, http_request.version);
    }

    void ParseHttpRequestHeader()
    {
        string key;
        string value;
        for (auto &iter : http_request.request_header)
        {
            if (Util::CutString(iter, key, value, SEP))
            {
                http_request.header_kv.insert({key, value});
            }
            // cout << "debug  " << key << endl;
            // cout << "debug  " << value << endl;
        }
    }

    bool IsNeedRecvHttpRequestBody()
    {
        auto &method = http_request.method;
        if (method == "POST")
        {
            auto &header_kv = http_request.header_kv;
            auto iter = header_kv.find("Content-Length");
            if (iter != header_kv.end())
            {
                http_request.content_length = atoi(iter->second.c_str());
                return true;
            }
        }
        return false;
    }

    void RecvHttpRequestBody()
    {
        if (IsNeedRecvHttpRequestBody())
        {
            int content_length = http_request.content_length;
            auto &body = http_request.reques_body;
            char ch = 0;
            while (content_length)
            {
                ssize_t s = recv(sock, &ch, 1, 0);
                if (s > 0)
                {
                    body.push_back(ch);
                    content_length--;
                }
                else
                    break;
            }
        }
    }

    int ProcessNonCgi(int size)
    {
        http_response.fd = open(http_request.path.c_str(), O_RDONLY);
        if (http_response.fd >= 0)
        {
            http_response.status_line = HTTP_VERSION;
            http_response.status_line += " ";
            http_response.status_line += std::to_string(http_response.status_code);
            http_response.status_line += " ";
            http_response.status_line += Code2Desc(http_response.status_code);
            http_response.status_line += LINE_END;
            http_response.size = size;
            return OK;
        }

        return 404;
    }

public:
    EndPoint(int _sock) : sock(_sock)
    {
    }
    void RecvHttpRequest()
    {
        RecvHttpRequestLine();
        RecvHttpRequestHeader();
        ParseHttpRequestLine();
        ParseHttpRequestHeader();
        RecvHttpRequestBody();
    }

    void BuildHttpResponse()
    {
        auto &code = http_response.status_code;
        string _path;
        int size = 0;
        struct stat st;
        if (http_request.method != "GET" && http_request.method != "POST")
        {
            // 非法请求
            LOG(WARINING, "method is not right");
            code = NOT_FOUND;
            goto END;
        }
        if (http_request.method == "GET")
        {
            size_t pos = http_request.uri.find('?');
            if (pos != string::npos)
            {
                Util::CutString(http_request.uri, http_request.path, http_request.query_string, "?");
                http_request.cgi = true;
            }
            else
                http_request.path = http_request.uri;
        }
        else if (http_request.method == "POST")
        {
            http_request.cgi = true;
        }
        else
        {
        }

        // cout << "debug uri : " << http_request.uri << endl;
        // cout << "debug path : " << http_request.path << endl;
        // cout << "debug query_string : " << http_request.query_string << endl;

        _path = http_request.path;
        http_request.path = WEB_ROOT;
        http_request.path += _path;
        // cout << "debug path :" << http_request.path << endl;

        if (http_request.path[http_request.path.size() - 1] == '/')
            http_request.path += HOME_PAGE;
        // cout << "debug path :" << http_request.path << endl;

        if (stat(http_request.path.c_str(), &st) == 0)
        {
            // 资源存在
            if (S_ISDIR(st.st_mode))
            {
                // 请求的资源是一个目录 不被允许 需要处理
                http_request.path += "/";
                http_request.path += HOME_PAGE;
                stat(http_request.path.c_str(), &st);
            }
            else if ((st.st_mode & S_IXUSR) || (st.st_mode & S_IXGRP) || (st.st_mode & S_IXOTH))
            {
                // 特殊处理
                http_request.cgi = true;
            }
            size = st.st_size;
        }
        else
        {
            // 资源不存在
            string info = http_request.path;
            info += " Not Found!";
            LOG(INFO, info);
            code = NOT_FOUND;
            goto END;
        }

        if (http_request.cgi)
        {
            // ProcessCgi();
        }
        else
        {
            code = ProcessNonCgi(size); // 简单的网页返回  返回静态网页
        }

    END:
        if (code != OK)
        {
        }
        return;
    }

    void SendHttpResponse()
    {
        send(sock, http_response.status_line.c_str(), http_response.status_line.size(), 0);
        for (auto iter : http_response.response_header)
        {
            send(sock, iter.c_str(), iter.size(), 0);
        }
        send(sock, http_response.blank.c_str(), http_response.blank.size(), 0);
        sendfile(sock, http_response.fd, nullptr, http_response.size);
        close(http_response.fd);
    }
    ~EndPoint()
    {
        close(sock);
    }
};

class Entrance
{
public:
    static void *HandlerRequest(void *_sock)
    {
        LOG(INFO, "GET A NEW LINK");
        LOG(INFO, "Handler Request Begin");
        int sock = *(int *)_sock;
        delete (int *)_sock;
        // cout << "get a new link ..." << sock << endl;

#ifdef DEBUG
        // Test
        char buffer[4096];
        recv(sock, buffer, sizeof(buffer), 0);
        cout << "--------begin---------" << endl;
        cout << buffer << endl;
        cout << "---------end----------" << endl;
#else
        EndPoint *ep = new EndPoint(sock);
        ep->RecvHttpRequest();
        // ep->ParseHttpRequest();
        ep->BuildHttpResponse();
        ep->SendHttpResponse();
        delete ep;
#endif

        // string line;
        // Util::ReadLine(sock, line);
        // cout << line << endl;

        // close(sock);
        // return nullptr;

        LOG(INFO, "Handler Request End");
    }
};